#ifndef _NTH_LIB_H
#include "nth-lib.h"
#endif

#ifndef _STDLIB_H
#include <stdlib.h>
#endif

#ifndef _STRING_H
#include <string.h>
#endif

#ifndef _WINDOWS_H
#include <windows.h>
#endif

int Export_Nth_Directory(
	char * nth_path
){
	BOOL result = 0;

	result = CreateDirectory(
		nth_path,
		0
	);

	if (!result){
		return 1;
	}

	return 0;
}

int Link_Nth_Plugins(struct Nth_Program * nth_program){

	int error = 0;

	char * nth_path = 0;
	char * nth_plugin_path = 0;

	HANDLE search_handle;
	WIN32_FIND_DATA file_data;

	BOOL success = 0;

	if (!nth_program){
		return NTH_ERROR_NULL_POINTER;
	}

	nth_path = getenv("NTH_DIR");

	if (!nth_path){

		Push_Nth_Failure(
			nth_program,
			"Link_Nth_Plugins",
			"NTH_DIR is not set"
		);

		return 1;
	}

	error = Create_Nth_Path(&nth_plugin_path);

	if (error){

		Push_Nth_Failure(
			nth_program,
			"Link_Nth_Plugins",
			"Failed to create plugin path"
		);

		return 1;
	}

	error = Set_Nth_Path_Prefix(
		&nth_plugin_path,
		nth_path
	);

	if (error){

		Destroy_Nth_Path(&nth_plugin_path);

		Push_Nth_Failure(
			nth_program,
			"Link_Nth_Plugins",
			"Failed to set plugin prefix of path"
		);

		return 1;
	}

	success = SetDllDirectory(
		nth_plugin_path
	);

	if (!success){

		Destroy_Nth_Path(&nth_plugin_path);

		Push_Nth_Failure(
			nth_program,
			"Link_Nth_Plugins",
			"Failed to set plugin dll directory"
		);

		return 1;
	}

	error = Set_Nth_Path_Suffix(
		&nth_plugin_path,
		"nth-plugin--*.dll"
	);

	if (error){

		Destroy_Nth_Path(&nth_plugin_path);

		Push_Nth_Failure(
			nth_program,
			"Link_Nth_Plugins",
			"Failed to set plugin suffix of path"
		);

		return 1;
	}

	search_handle = FindFirstFile(
		nth_plugin_path,
		&file_data
	);

	if (search_handle == INVALID_HANDLE_VALUE){

		Destroy_Nth_Path(&nth_plugin_path);

		Push_Nth_Failure(
			nth_program,
			"Link_Nth_Plugins",
			"Failed to find file in plugin path"
		);

		return 1;
	}

	success = 1;

	for (;success;){

		HMODULE library_handle = 0;

		struct Nth_Plugin plugin;

		typedef int (*plugin_call)(struct Nth_Plugin *);

		int (*init_plugin)(struct Nth_Plugin *) = 0;
		int (*clean_plugin)(struct Nth_Plugin *) = 0;

		library_handle = LoadLibrary(
			file_data.cFileName
		);

		if (!library_handle){

			success = FindNextFile(
				search_handle,
				&file_data
			);

			continue;
		}

		init_plugin = (plugin_call) GetProcAddress(
			library_handle,
			"Initialize_Nth_Plugin"
		);

		if (!init_plugin){

			printf("%s nope\n", file_data.cFileName);

			FreeLibrary(library_handle);

			success = FindNextFile(
				search_handle,
				&file_data
			);

			continue;
		}

		clean_plugin = (plugin_call) GetProcAddress(
			library_handle,
			"Clean_Nth_Plugin"
		);

		if (!clean_plugin){

			FreeLibrary(library_handle);

			success = FindNextFile(
				search_handle,
				&file_data
			);

			continue;
		}

		memset(&plugin, 0, sizeof(plugin));

		plugin.library_handle = library_handle;

		error = init_plugin(&plugin);

		if (error){

			FreeLibrary(library_handle);

			success = FindNextFile(
				search_handle,
				&file_data
			);

			continue;
		}

		error = Push_Nth_Plugin(
			nth_program,
			&plugin
		);

		if (error){

			clean_plugin(&plugin);
			FreeLibrary(library_handle);

			Push_Nth_Failure(
				nth_program,
				"Link_Nth_Plugins",
				"Failed to push plugin"
			);

			return 1;
		}

		success = FindNextFile(
			search_handle,
			&file_data
		);
	}

	Destroy_Nth_Path(&nth_plugin_path);
	FindClose(search_handle);

	return 0;
}

int Unlink_Nth_Plugins(
	struct Nth_Program * nth_program
){
	size_t plugin_index = 0;
	size_t plugin_count = 0;

	struct Nth_Plugin * plugin_array = 0;

	if (!nth_program){
		return NTH_ERROR_NULL_POINTER;
	}

	plugin_index = 0;
	plugin_count = nth_program->plugin_count;
	plugin_array = nth_program->plugin_array;

	if (!plugin_array){
		return 0;
	}

	while (plugin_index < plugin_count){

		typedef int (*plugin_call)(struct Nth_Plugin *);

		int (*clean_plugin)(struct Nth_Plugin *) = 0;

		clean_plugin = (plugin_call) GetProcAddress(
			plugin_array[plugin_index].library_handle,
			"Clean_Nth_Plugin"
		);

		if (clean_plugin){
			clean_plugin(&plugin_array[plugin_index]);
		}

		if (plugin_array[plugin_index].library_handle){
			FreeLibrary(plugin_array[plugin_index].library_handle);
		}

		plugin_index++;
	}

	free(nth_program->plugin_array);

	nth_program->plugin_array = 0;
	nth_program->plugin_count = 0;

	return 0;
}


